#include "FFMpegDemuxerWindow.h"

FFMpegDemuxerWindow::FFMpegDemuxerWindow(QWidget* parent)
	: QWidget(parent)
{
	this->resize(QSize(320, 480));
	this->setWindowTitle("ffmpeg解封装");
	this->setWindowIcon(QIcon("images/opencv.png"));

	QPushButton* btnMuxer = new QPushButton(this);
	btnMuxer->setText("解封装");
	connect(btnMuxer, &QPushButton::clicked, [=]() {
		demuxer();
		});
}
//fps转换
static double r2d(AVRational r) {
	return r.den == 0 ? 0 : (double)r.num / (double)r.den;
}
//休眠
void XSleep(int ms) {
	//c++ 11
	std::chrono::milliseconds du(ms);
	std::this_thread::sleep_for(du);
}
void FFMpegDemuxerWindow::demuxer() {
	const char* filePath = "C:/Users/DBF-DEV-103/Downloads/1e2fced773f0583e7ae85e08461ec384.mp4";

	//初始化网络库 （可以打开rtsp rtmp http 协议的流媒体视频）
	avformat_network_init();

	//设置参数
	AVDictionary* opts = NULL;
	//设置rtsp流，以tcp协议打开
	av_dict_set(&opts, "rtsp_transport", "tcp", 0);
	//设置网络延时时间
	av_dict_set(&opts, "max_delay", "500", 0);

	//解封装上下文
	AVFormatContext* mFormatContext = NULL;
	int ret = avformat_open_input(&mFormatContext, filePath,
		0,//0表示自动选择封装器
		&opts//设置参数，比如rtsp的延时时间
	);

	if (ret != 0) {
		char buf[1024] = { 0 };
		av_strerror(ret, buf, sizeof(buf) - 1);
		qDebug() << "打开媒体文件失败:" << buf;
		return;
	}
	qDebug() << "媒体文件打开成功";

	//获取流信息
	ret = avformat_find_stream_info(mFormatContext, 0);
	if (ret != 0) {
		char buf[1024] = { 0 };
		av_strerror(ret, buf, sizeof(buf) - 1);
		qDebug() << "超找流信息失败:" << buf;
		return;
	}
	///总时长->毫秒
	int totalMs = mFormatContext->duration / (AV_TIME_BASE / 1000);
	qDebug() << "totalMs:" << totalMs;
	//打印视频流详细信息
	//av_dump_format(mFormatContext, 0, filePath, 0);

	//获取音频和视频流
	//出丝滑音频及视频流id
	int videoStream = 0;
	int audioStream = 1;
	//获取音视频流信息（遍历，函数获取）
	for (int i = 0;i < mFormatContext->nb_streams;i++) {
		AVStream* as = mFormatContext->streams[i];//获取流
		qDebug() << "codec_id=" << as->codecpar->codec_id;//解码器id
		qDebug() << "format=" << as->codecpar->format;//解码格式
		//音频 AVMEDIA_TYPE_AUDIO
		if (as->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			audioStream = i;
			qDebug() << "音频信息";
			qDebug() << "sample_rate=" << as->codecpar->sample_rate;//采样率
			qDebug() << "channels=" << as->codecpar->channels;//声道数
			//一帧数据
			qDebug() << "frame_size=" << as->codecpar->frame_size;
		}
		else if (as->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			videoStream = i;
			qDebug() << "视频信息";
			qDebug() << "width=" << as->codecpar->width;
			qDebug() << "height=" << as->codecpar->height;
			//fps
			qDebug() << "video fps=" << r2d(as->avg_frame_rate);
		}
	}

	//获取视频流
	videoStream = av_find_best_stream(mFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
	//发现视频解码器
	const AVCodec* mVideoCodec = avcodec_find_decoder(mFormatContext->streams[videoStream]->codecpar->codec_id);
	if (!mVideoCodec) {
		qDebug() << "can't find the video codec id" << mFormatContext->streams[videoStream]->codecpar->codec_id;
		return;
	}
	qDebug() << "find the video codec id=" << mFormatContext->streams[videoStream]->codecpar->codec_id;
	//创建，解码器上下文（并给解码器上下文分配空间）
	AVCodecContext* mVideoCodecContext = avcodec_alloc_context3(mVideoCodec);
	//配置解码器上下文参数.（将流中解码器参数赋值给解码器上下文）
	avcodec_parameters_to_context(mVideoCodecContext, mFormatContext->streams[videoStream]->codecpar);
	//设置8线程解码
	mVideoCodecContext->thread_count = 8;
	//打开解码器上下文
	ret = avcodec_open2(mVideoCodecContext, NULL, NULL);
	if (ret != 0)
	{
		char buf[1024] = { 0 };
		av_strerror(ret, buf, sizeof(buf) - 1);
		qDebug() << "avcodec_open2  video failed! :" << buf;
		return;
	}
	// 
	qDebug() << "avcodec_open2 video success!!";


	//打开音频解码器
	const AVCodec* mAudioCodec = avcodec_find_decoder(mFormatContext->streams[audioStream]->codecpar->codec_id);
	if (!mAudioCodec) {
		qDebug() << "查找音频解码器失败=" << mFormatContext->streams[audioStream]->codecpar->codec_id;
	}
	qDebug() << "打开音频解码器成功";
	//创建音频解码器上下文
	AVCodecContext* mAudioCodecContext = avcodec_alloc_context3(mAudioCodec);
	//copy参数给音频解码器
	avcodec_parameters_to_context(mAudioCodecContext, mFormatContext->streams[audioStream]->codecpar);
	//8线程解码
	mAudioCodecContext->thread_count = 8;
	//打开解码器上下文
	ret = avcodec_open2(mAudioCodecContext, NULL, NULL);
	if (ret != 0) {
		char buf[1024] = { 0 };
		av_strerror(ret, buf, sizeof(buf) - 1);
		qDebug() << "avcodec_open2 audio  failed! :" << buf;
		return;
	}
	qDebug() << "avcodec_open2 audio success";



	//初始化AVPacket
	AVPacket* pkt = av_packet_alloc();
	AVFrame* frame = av_frame_alloc();//初始化AVFrame

	//像素和尺寸格式转换上下文
	SwsContext* swsContext = NULL;
	unsigned char* rgb = NULL;

	//音频重采样上下文,初始化
	SwrContext* swrContext = swr_alloc();
	swrContext = swr_alloc_set_opts(
		swrContext,
		av_get_default_channel_layout(2),//输出格式
		AV_SAMPLE_FMT_S16,//输出采样格式
		mAudioCodecContext->sample_rate,//输出采样率
		av_get_default_channel_layout(mAudioCodecContext->channels),//输入声道数
		mAudioCodecContext->sample_fmt,//输入采样格式
		mAudioCodecContext->sample_rate//输入采样率
		, 0, 0
	);
	ret = swr_init(swrContext);
	if (ret != 0)
	{
		//输出错误日志
		char buf[1024] = { 0 };
		av_strerror(ret, buf, sizeof(buf) - 1);
		qDebug() << "swr_init  failed! :" << buf;
		return;
	}
	unsigned char* pcm = NULL;//存储音频裸数据

	for (;;) {
		ret = av_read_frame(mFormatContext, pkt);
		if (ret != 0) {//如果读取失败休眠3s后就循环读
			//循环播放
			qDebug() << "==============================end==============================";
			int ms = 3000; //三秒位置 根据时间基数（分数）转换
			long long pos = (double)ms / (double)1000 * r2d(mFormatContext->streams[pkt->stream_index]->time_base);
			av_seek_frame(mFormatContext, videoStream, pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);
			continue;
		}
		//packet的大小
		qDebug() << "pkt->size =" << pkt->size;
		//pts
		qDebug() << "pkt->pts=" << pkt->pts;
		//pts->转毫秒
		qDebug() << pkt->pts * (r2d(mFormatContext->streams[pkt->stream_index]->time_base) * 1000);

		//解码时间
		qDebug() << "pkt->dts=" << pkt->dts;
		AVCodecContext* mCodecContext = NULL;
		if (pkt->stream_index == videoStream) {
			qDebug() << "视频";
			mCodecContext = mVideoCodecContext;
		}
		else if (pkt->stream_index == audioStream) {
			qDebug() << "音频";
			mCodecContext = mAudioCodecContext;
		}
		//解码视频
		//发送packet到解码线程，send传NULL后调用多次receive取出所有缓冲帧
		ret = avcodec_send_packet(mCodecContext, pkt);
		//释放，引用计数-1，为0时释放空间
		av_packet_unref(pkt);
		if (ret != 0) {
			char buf[1024] = { 0 };
			av_strerror(ret, buf, sizeof(buf) - 1);
			qDebug() << "avcodec_send_packet  failed! :" << buf;
			continue;
		}
		for (;;) {
			//从线程中获取解码接口，一次send可以多次receive
			ret = avcodec_receive_frame(mCodecContext, frame);
			if (ret != 0) {
				break;
			}
			qDebug() << "recv frame " << frame->format << " " << frame->linesize[0];
			//视频
			if (mCodecContext == mVideoCodecContext) {
				swsContext = sws_getCachedContext(swsContext,
					frame->width, frame->height,//输入的宽高
					(AVPixelFormat)frame->format,//输入格式
					frame->width, frame->height,//输出的宽高
					AV_PIX_FMT_RGBA,//输出格式RGBA
					SWS_BILINEAR,
					0, 0, 0
				);
				if (swsContext) {
					if (!rgb) {
						rgb = new unsigned char[frame->width * frame->height * 4];
					}
					uint8_t* data[2] = { 0 };
					data[0] = rgb;
					int lines[2] = { 0 };
					lines[0] = frame->width * 4;
					ret = sws_scale(swsContext,
						frame->data,
						frame->linesize,
						0,
						frame->height,
						data,
						lines);
					qDebug() << "sws_scale=" << ret;
				}
			}
			else//音频
			{
				uint8_t* data[2] = { 0 };
				if (!pcm) {
					pcm = new uint8_t[frame->nb_samples * 2 * 2];
				}
				data[0] = pcm;
				ret = swr_convert(swrContext,
					data, frame->nb_samples,//输出
					(const uint8_t**)frame->data, frame->nb_samples//输入
				);
				qDebug() << "swr_convert = " << ret;
			}
		}




	}
	av_frame_free(&frame);
	av_packet_free(&pkt);

	///release
	if (swrContext) {
		swr_free(&swrContext);
		swrContext = NULL;
	}
	if (swsContext) {
		sws_freeContext(swsContext);
		swsContext = NULL;
	}
	///清理封装上下文
	if (mFormatContext) {
		avformat_close_input(&mFormatContext);
		mFormatContext = NULL;
	}

}
void FFMpegDemuxerWindow::release() {

}

void FFMpegDemuxerWindow::playAudio() {
	QAudioFormat fmt;
	fmt.setSampleRate(44100);
	fmt.setSampleSize(16);
	fmt.setChannelCount(2);
	fmt.setCodec("audio/pcm");
	fmt.setByteOrder(QAudioFormat::LittleEndian);
	fmt.setSampleType(QAudioFormat::UnSignedInt);
	QAudioOutput* out = new QAudioOutput(fmt);
	QIODevice* io = out->start(); //开始播放

	int size = out->periodSize();
	char* buf = new char[size];

	FILE* fp = fopen("out.pcm", "rb");
	while (!feof(fp))
	{
		if (out->bytesFree() < size)
		{
			QThread::msleep(1);
			continue;
		}
		int len = fread(buf, 1, size, fp);
		if (len <= 0)break;
		io->write(buf, len);
	}
	fclose(fp);
	delete buf;
	buf = 0;
}
FFMpegDemuxerWindow::~FFMpegDemuxerWindow()
{

}
